//
//  JRKeyChain.swift
//  JRKeyChain
//
//  Created by 京睿 on 2017/6/9.
//  Copyright © 2017年 JingRuiWangKe. All rights reserved.
//

import Foundation
import Security

private enum AttributesType {
    case delete, update, select
}

public class JRKeyChain {
    
    private init() { }
    
    private typealias this = JRKeyChain
    
    private static let jSecClass           = String(kSecClass)
    private static let jSecAttrService     = String(kSecAttrService)
    private static let jSecAttrAccount     = String(kSecAttrAccount)
    private static let jSecAttrAccessible  = String(kSecAttrAccessible)
    private static let jSecReturnData      = String(kSecReturnData)
    private static let jSecMatchLimit      = String(kSecMatchLimit)
    private static let jSecValueData       = String(kSecValueData)
    
    private static let attributes: [String: Any] = [
        jSecClass: kSecClassGenericPassword,
        jSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlock
    ]
    
    private static let getAttributes: [String: Any] = [
        jSecClass: kSecClassGenericPassword,
        jSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlock,
        jSecReturnData: kCFBooleanTrue,
        jSecMatchLimit: kSecMatchLimitOne
    ]
    
    @discardableResult
    public class func update(_ key: String, value: Any) -> Bool {
        var query = addKey(by: .update, key: key)
        delete(key)
        query[jSecValueData] = NSKeyedArchiver.archivedData(withRootObject: value)
        return SecItemAdd(query as CFDictionary, nil) == errSecSuccess
    }
    
    public class func select(_ key: String) -> Any? {
        let query = addKey(by: .select, key: key)
        var result: AnyObject?
        let status = SecItemCopyMatching(query as CFDictionary, &result)
        guard status == errSecSuccess else {
            return nil
        }
        guard let data = result as? Data else { return nil }
        return NSKeyedUnarchiver.unarchiveObject(with: data)
    }
    
    @discardableResult
    public class func delete(_ key: String) -> Bool {
        let query = addKey(by: .delete, key: key)
        return SecItemDelete(query as CFDictionary) == errSecSuccess
    }
    
    private class func addKey(by type: AttributesType, key: String) -> [String: Any] {
        var normal: [String: Any]!
        switch type {
        case .select:
            normal = getAttributes
        default:
            normal = attributes
        }
        normal[jSecAttrService] = key
        normal[jSecAttrAccount] = key
        return normal
    }
}
